//
// Created by cnsunrun on 2018/3/26.
//

#include <jni.h>
#include <string.h>
#include <malloc.h>
#include "app-const.h"
#include <Android/log.h>

#define TAG "app-utils-log" // 这个是自定义的LOG的标识
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,TAG ,__VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG ,__VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,TAG ,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG ,__VA_ARGS__)
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,TAG ,__VA_ARGS__)
#ifndef APIEXERCISE_APP_UTILS_H
#define APIEXERCISE_APP_UTILS_H

#endif //APIEXERCISE_APP_UTILS_H


/**
 * 字符串数组转 byte数组
 * @param env
 * @param chars
 * @return
 */
jbyteArray convert_char(JNIEnv *env, const char *chars) {
    int strLen = strlen(chars);
    jbyteArray byteArray = env->NewByteArray(strLen);
    env->SetByteArrayRegion(byteArray, 0, strLen, reinterpret_cast<const jbyte *>(chars));
    return byteArray;
}

jbyte* convert_byte(JNIEnv *env, const char *chars) {
    int strLen = sizeof(chars);
    jbyte *temp = (jbyte *) malloc(strLen);
    memcpy(temp, chars, strLen);
    return temp;
}

char *jstringTochars(JNIEnv *env, jstring jstr) {
    char *pStr = NULL;
    jclass jstrObj = env->FindClass("java/lang/String");
    jstring encode = env->NewStringUTF("utf-8");
    jmethodID methodId = env->GetMethodID(jstrObj, "getBytes", "(Ljava/lang/String;)[B");
    jbyteArray byteArray = (jbyteArray) env->CallObjectMethod(jstr, methodId, encode);
    jsize strLen = env->GetArrayLength(byteArray);
    jbyte *jBuf = env->GetByteArrayElements(byteArray, JNI_FALSE);
    if (sizeof(jBuf) > 0) {
        pStr = (char *) malloc(strLen + 1);
        if (!pStr) {
            return NULL;
        }
        memcpy(pStr, jBuf, strLen);
        pStr[strLen] = 0;
    }

    env->ReleaseByteArrayElements(byteArray, jBuf, 0);

    return pStr;
}

//C字符串转java字符串
jstring charsToJstring(JNIEnv *env, const char *pStr) {
    int strLen = strlen(pStr);
    jclass jstrObj = env->FindClass("java/lang/String");
    jmethodID methodId = env->GetMethodID(jstrObj, "<init>", "([BLjava/lang/String;)V");
    jbyteArray byteArray = env->NewByteArray(strLen);
    jstring encode = env->NewStringUTF("utf-8");
    env->SetByteArrayRegion(byteArray, 0, strLen, (jbyte *) pStr);
    return (jstring) env->NewObject(jstrObj, methodId, byteArray, encode);
}


/**
 * 把java的string转化成c的字符串
 */
std::string Jstring2string(JNIEnv *env, jstring jstr) {
    char *rtn = NULL;
    jclass clsstring = env->FindClass("java/lang/String");  //String
    jstring strencode = env->NewStringUTF("GB2312"); //"gb2312"
    jmethodID mid = env->GetMethodID(clsstring, "getBytes",
                                     "(Ljava/lang/String;)[B"); //getBytes(Str);
    jbyteArray barr = (jbyteArray) env->CallObjectMethod(jstr, mid,
                                                         strencode); // String .getByte("GB2312");
    jsize alen = env->GetArrayLength(barr);
    jbyte *ba = env->GetByteArrayElements(barr, JNI_FALSE);
    if (alen > 0) {
        rtn = (char *) malloc(alen + 1);         //"\0"
        memcpy(rtn, ba, alen);
        rtn[alen] = 0;
    }
    env->ReleaseByteArrayElements(barr, ba, 0);  //释放内存空间
    return rtn;
}

/**
 * C字符串转java字符串
 */
jstring str2Jstring(JNIEnv *env, const char *pStr) {
    int strLen = strlen(pStr);
    jclass jstrObj = env->FindClass("java/lang/String");
    jmethodID methodId = env->GetMethodID(jstrObj, "<init>", "([BLjava/lang/String;)V");
    jbyteArray byteArray = env->NewByteArray(strLen);
    jstring encode = env->NewStringUTF("utf-8");
    env->SetByteArrayRegion(byteArray, 0, strLen, (jbyte *) pStr);
    return (jstring) env->NewObject(jstrObj, methodId, byteArray, encode);
}


/**
 * 加密byte数组
 * @param env
 * @param content_
 * @param encrypt_val 加密用的值
 * @return
 */
jbyteArray encrypt(JNIEnv *env, jbyteArray content_, const char *encrypt_val) {
    jbyte *content = env->GetByteArrayElements(content_, NULL);

    //声明局部变量
    int index = 0;
    int size = env->GetArrayLength(content_);
    int encrypt_size = sizeof(encrypt_val);
    LOGE("size%d", size);

    for (index = 0; index < size; index++) {
        LOGE("content1 %d", content[index]);
        content[index] = content[index] ^ encrypt_val[index % encrypt_size];
        LOGE("content2 %d", content[index]);
    }
    jbyteArray result_arr = env->NewByteArray(size);
    env->SetByteArrayRegion(result_arr, 0, size, content);
    env->ReleaseByteArrayElements(content_, content, 0);
    return result_arr;
}




jobject getApplication(JNIEnv *env) {
    jclass localClass = env->FindClass("android/app/ActivityThread");
    if (localClass != NULL) {
        // LOGI("class have find");
        jmethodID getapplication = env->GetStaticMethodID(localClass, "currentApplication",
                                                          "()Landroid/app/Application;");
        if (getapplication != NULL) {
            jobject application = env->CallStaticObjectMethod(localClass, getapplication);
            return application;
        }
        return NULL;
    }
    return NULL;
}

void exitApplication(JNIEnv *env, jint flag) {
    jclass temp_clazz = NULL;
    jmethodID mid_static_method;
    // 1、从classpath路径下搜索ClassMethod这个类，并返回该类的Class对象
    temp_clazz = env->FindClass("java/lang/System");
    mid_static_method = env->GetStaticMethodID(temp_clazz, "exit", "(I)V");
    env->CallStaticVoidMethod(temp_clazz, mid_static_method, flag);
    env->DeleteLocalRef(temp_clazz);
}


/**
 * 初始化时验证app信息
 * @param env
 * @param thiz2
 * @return
 */
bool init(JNIEnv *env, jobject thiz2) {
    //获取到Context
    jobject context = getApplication(env);
    jclass activity = env->GetObjectClass(context);
    // 得到 getPackageManager 方法的 ID
    jmethodID methodID_func = env->GetMethodID(activity, "getPackageManager",
                                               "()Landroid/content/pm/PackageManager;");
    // 获得PackageManager对象
    jobject packageManager = env->CallObjectMethod(context, methodID_func);
    jclass packageManagerclass = env->GetObjectClass(packageManager);
    //得到 getPackageName 方法的 ID
    jmethodID methodID_pack = env->GetMethodID(activity, "getPackageName", "()Ljava/lang/String;");
    //获取包名
    jstring name_str = (jstring) (env->CallObjectMethod(context, methodID_pack));
    if (strcmp(jstringTochars(env, name_str), packnamefinal) != 0) {
        //检查包名是否符合
//        LOGE("验证不通过！");
//        exitApplication(env, 0);
    }
    // 得到 getPackageInfo 方法的 ID
    jmethodID methodID_pm = env->GetMethodID(packageManagerclass, "getPackageInfo",
                                             "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");
    // 获得应用包的信息
    jobject package_info = env->CallObjectMethod(packageManager, methodID_pm, name_str, 64);
    // 获得 PackageInfo 类
    jclass package_infoclass = env->GetObjectClass(package_info);
    // 获得签名数组属性的 ID
    jfieldID fieldID_signatures = env->GetFieldID(package_infoclass, "signatures",
                                                  "[Landroid/content/pm/Signature;");
    // 得到签名数组，待修改
    jobject signatur = env->GetObjectField(package_info, fieldID_signatures);
    jobjectArray signatures = (jobjectArray) (signatur);
    // 得到签名
    jobject signature = env->GetObjectArrayElement(signatures, 0);
    // 获得 Signature 类，待修改
    jclass signature_clazz = env->GetObjectClass(signature);
    //---获得签名byte数组
    jmethodID tobyte_methodId = env->GetMethodID(signature_clazz, "toByteArray", "()[B");
    jbyteArray signature_byte = (jbyteArray) env->CallObjectMethod(signature, tobyte_methodId);
    //把byte数组转成流
    jclass byte_array_input_class = env->FindClass("java/io/ByteArrayInputStream");
    jmethodID init_methodId = env->GetMethodID(byte_array_input_class, "<init>", "([B)V");
    jobject byte_array_input = env->NewObject(byte_array_input_class, init_methodId,
                                              signature_byte);
    //实例化X.509
    jclass certificate_factory_class = env->FindClass("java/security/cert/CertificateFactory");
    jmethodID certificate_methodId = env->GetStaticMethodID(certificate_factory_class,
                                                            "getInstance",
                                                            "(Ljava/lang/String;)Ljava/security/cert/CertificateFactory;");
    jstring x_509_jstring = env->NewStringUTF("X.509");
    jobject cert_factory = env->CallStaticObjectMethod(certificate_factory_class,
                                                       certificate_methodId, x_509_jstring);
    //certFactory.generateCertificate(byteIn);
    jmethodID certificate_factory_methodId = env->GetMethodID(certificate_factory_class,
                                                              "generateCertificate",
                                                              ("(Ljava/io/InputStream;)Ljava/security/cert/Certificate;"));
    jobject x509_cert = env->CallObjectMethod(cert_factory, certificate_factory_methodId,
                                              byte_array_input);

    jclass x509_cert_class = env->GetObjectClass(x509_cert);
    jmethodID x509_cert_methodId = env->GetMethodID(x509_cert_class, "getEncoded", "()[B");
    jbyteArray cert_byte = (jbyteArray) env->CallObjectMethod(x509_cert, x509_cert_methodId);

    //MessageDigest.getInstance("SHA1")
    jclass message_digest_class = env->FindClass("java/security/MessageDigest");
    jmethodID methodId = env->GetStaticMethodID(message_digest_class, "getInstance",
                                                "(Ljava/lang/String;)Ljava/security/MessageDigest;");
    jstring sha1_jstring = env->NewStringUTF("MD5");
    jobject sha1_digest = env->CallStaticObjectMethod(message_digest_class, methodId, sha1_jstring);
    methodId = env->GetMethodID(message_digest_class, "digest", "([B)[B");
    jbyteArray sha1_byte = (jbyteArray) env->CallObjectMethod(sha1_digest, methodId, cert_byte);
    jsize array_size = env->GetArrayLength(sha1_byte);
    jbyte *sha1 = env->GetByteArrayElements(sha1_byte, NULL);
    char hex_sha[array_size * 2 + 1];
    int i;
    for (i = 0; i < array_size; ++i) {
        hex_sha[2 * i] = HexCode[((unsigned char) sha1[i]) / 16];
        hex_sha[2 * i + 1] = HexCode[((unsigned char) sha1[i]) % 16];
    }
    hex_sha[array_size * 2] = '\0';
    LOGD("sin-sha1:%s", hex_sha);
    //const char *sign = env->GetStringUTFChars(signstr, NULL);
    if (strcmp(hex_sha, sha1final) != 0) {
        LOGE("验证不通过！");
        exitApplication(env, 0);
    } else {
        // 获得_CONTEXT字段 ID
//        jfieldID fieldID_cotext = env->GetStaticFieldID(activity, "_CONTEXT",
//                                                        "Lcom/quanwe/common/base/App;");
        //初始化该值
        //env->SetObjectField(thiz, fieldID_cotext, thiz);
//        env->SetStaticObjectField(activity, fieldID_cotext, context);
        LOGD("验证通过！");
        return true;
    }
    return false;
}



